/*
 * AIROHA RF driver for WinBond WiFi on Linux 2.6
 *
 * Written by Ferenc Csicsova & Karoly Kasza
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/usb.h>
#include <net/mac80211.h>
#include <linux/etherdevice.h>

#include "wbusb.h"
#include "wbusb_airoha.h"


u32 al2230_rf_data[]     =
{
    (0x00<<20)|0x09EFC,
    (0x01<<20)|0x8CCCC,
    (0x02<<20)|0x40058,// 20060419 0x401D8,
    (0x03<<20)|0xCFFF0,
    (0x04<<20)|0x24100,// 20060419 0x23800,
    (0x05<<20)|0xA3B2F,// 20060419 0xA3B72,
    (0x06<<20)|0x6DA01,
    (0x07<<20)|0xE3628,// 20060419 0xE1688,
    (0x08<<20)|0x11600,
    (0x09<<20)|0x9DC02,// 20060419 0x97602,//0x99E02, //0x9AE02
    (0x0A<<20)|0x5DDB0,// 941206 For QCOM interference 0x588b0,//0x5DDB0, 940601 adj 0x5aa30 for bluetooth
    (0x0B<<20)|0xD9900,
    (0x0C<<20)|0x3FFBD,
    (0x0D<<20)|0xB0000,
    (0x0F<<20)|0xF01A0 // 20060419 0xF00A0
};


void airoha_struct_init(struct wbusb_priv *priv)
{
	DEBUG("[airoha] airoha_struct_init")
	switch(priv->radio.type)
	{
		case RF_AIROHA_2230: //We can test only this one yet
			priv->radio.name="AIROHA 2230";
			DEBUG("[airoha] found 2230")
			break;
		case RF_AIROHA_7230:
			DEBUG("[airoha] found 7230")
			printk("AiroHA 7230 found, please contact developers");
//			priv->radio.name="AIROHA 7230";
			break;
		case RF_AIROHA_2230S:
			DEBUG("[airoha] found 2230S")
			printk("AiroHA 2230s found, please contact developers");
//			priv->radio.name="AIROHA 2230S";
			break;
	}
	priv->radio.start = airoha_start;
	priv->radio.rf_synt_init = airoha_rf_synt_init;
	priv->radio.bb_proc_init = airoha_bb_proc_init;
}

//The start is the same for all chips, except MAXIMs
//Start, called from ifconfig up, init_hw
int airoha_start(struct wbusb_priv *priv)
{
	int loop = 500;
	u32 tmp = 0;

	DEBUG("[airoha] airoha_start")

	//Taken from the old driver - dunno
	wbusb_reg_write_sync(priv, REG_U+0x1f4, 0xFF5807FF);

	//Regulator on only
	wbusb_reg_write_sync(priv, REG_U+0x1d4, 0x80);

	//10ms
	OS_SLEEP(10000);

	//plus flags
	wbusb_reg_write_sync(priv, REG_U+0x1d4, 0xb8);

	OS_SLEEP(10000);

	wbusb_reg_write_sync(priv, REG_U+0x1d0, 0x4968);

	//PLL_PD REF_PD set
	wbusb_reg_write_sync(priv, REG_U+0x1d4, 0xa0);

	OS_SLEEP(20000);

	while ( !(tmp & 0x20) && loop-- ) {
		if (!wbusb_reg_read_sync(priv, REG_U+0x01d0, &tmp)) break;
		OS_SLEEP(10000);
	}

	//MLK_EN
	wbusb_reg_write_sync(priv, REG_U+0x01d4, 0xe0);

	return 0;
}

int airoha_rf_synt_init(struct wbusb_priv *priv)
{
	u32	altmp[32];
	u32*	pltmp = altmp;
	u32	ltmp;
	u8	number=0x00;
	u8	i;

	DEBUG("[airoha] airoha_rf_synt_init")

	switch(priv->radio.type)
	{
		case RF_AIROHA_2230: //We can test only this one yet
			number = sizeof(al2230_rf_data)/sizeof(al2230_rf_data[0]);
			for( i=0; i<number; i++ )
			{
				//pHwData->phy_para[i] = al2230_rf_data[i];
				pltmp[i] = (1 << 31) | (0 << 30) | (20 << 24) | BitReverse( al2230_rf_data[i], 20);
			}

			//pHwData->phy_number = number;

			for( i=0; i<number; i++ )
				wbusb_reg_write_sync(priv,REG_M+0x64, pltmp[i]);


			ltmp = (1 << 31) | (0 << 30) | (20 << 24) | BitReverse( (0x07<<20)|0xE168E, 20);
			wbusb_reg_write_sync(priv,REG_M+0x64, ltmp);
			OS_SLEEP(10000);
			ltmp = (1 << 31) | (0 << 30) | (20 << 24) | BitReverse( al2230_rf_data[7], 20);
			wbusb_reg_write_sync(priv,REG_M+0x64, ltmp);
			OS_SLEEP(10000);
		case RF_AIROHA_2230S: //We can test only this one yet
			// regulator on only
			wbusb_reg_write_sync(priv,REG_U+0x1d4, 0x80);
			OS_SLEEP(10000);

			// PLL_PD REF_PD set to 0
			wbusb_reg_write_sync(priv,REG_U+0x1d4, 0xa0);
			OS_SLEEP(10000);

			// MLK_EN
			wbusb_reg_write_sync(priv,REG_U+0x1d4, 0xe0);
			// Reset hardware first
			wbusb_reg_write_sync(priv,REG_U+0x1b0, 0x01);
			OS_SLEEP(10000);
			//------------------------------------------------------------------------

			// The follow code doesn't use the burst-write mode
			//phy_set_rf_data(phw_data, 0x0F, (0x0F<<20) | 0xF01A0); //Raise Initial Setting
			ltmp = (1 << 31) | (0 << 30) | (20 << 24) | BitReverse( (0x0F<<20) | 0xF01A0, 20);
			wbusb_reg_write_sync(priv,REG_M+0x64, ltmp);

//			//Force TXI(Q)P(N) to commom mode level
//			Wb35Reg_WriteSync( pHwData, 0x1050, pHwData->Wb35Reg.BB50 | 0x1f );
			ltmp = priv->regs.BB5C & 0xfffff000;
			wbusb_reg_write_sync(priv,REG_BB+0x5c, ltmp);

			//!!TODO!!
			/*
			pHwData->Wb35Reg.BB50 |= 0x13;//(MASK_IQCAL_MODE|MASK_CALIB_START);//20060315.1 modify
			*/
			wbusb_reg_write_sync(priv,REG_BB+0x50, 0x13);
			OS_SLEEP(5000);

			//phy_set_rf_data(phw_data, 0x0F, (0x0F<<20) | 0xF01B0); //Activate Filter Cal. 
			ltmp = (1 << 31) | (0 << 30) | (20 << 24) | BitReverse( (0x0F<<20) | 0xF01B0, 20);
			wbusb_reg_write_sync(priv,REG_M+0x64, ltmp);
			OS_SLEEP(5000);

			//phy_set_rf_data(phw_data, 0x0F, (0x0F<<20) | 0xF01e0); //Activate TX DCC 
			ltmp = (1 << 31) | (0 << 30) | (20 << 24) | BitReverse( (0x0F<<20) | 0xF01E0, 20);
			wbusb_reg_write_sync(priv,REG_M+0x64, ltmp);
			OS_SLEEP(5000);

			//phy_set_rf_data(phw_data, 0x0F, (0x0F<<20) | 0xF01A0); //Resotre Initial Setting
			ltmp = (1 << 31) | (0 << 30) | (20 << 24) | BitReverse( (0x0F<<20) | 0xF01A0, 20);
			wbusb_reg_write_sync(priv,REG_M+0x64, ltmp);

//			//Force TXI(Q)P(N) to normal control
			//!!TODO!!
			/*
			Wb35Reg_WriteSync( pHwData, 0x105c, pHwData->Wb35Reg.BB5C );
			pHwData->Wb35Reg.BB50 &= ~0x13;//(MASK_IQCAL_MODE|MASK_CALIB_START);
			Wb35Reg_WriteSync( pHwData, 0x1050, pHwData->Wb35Reg.BB50);
			*/
			wbusb_reg_write_sync(priv,REG_BB+0x5c, 0x00);	//!!TODO!!
			wbusb_reg_write_sync(priv,REG_BB+0x50, 0x00);
			break;
	}
	DEBUG("[airoha] airoha_rf_synt_init end")
	return 0;
}

int airoha_bb_proc_init(struct wbusb_priv *priv)
{
        u32     pltmp[12];

	DEBUG("[airoha] airoha_bb_proc_init")
        
	switch(priv->radio.type)
	{
		case RF_AIROHA_7230:
			// 7230 -2.4GHz
			pltmp[0] = 0x16A8337A; // 0x16a5215f; // 0x1000 AGC_Ctrl1
			pltmp[1] = 0x9AFF9AA6; // 0x9aff9ca6; // 0x1004 AGC_Ctrl2
			pltmp[2] = 0x55D00A04; // 0x55d00a04; // 0x1008 AGC_Ctrl3
			pltmp[3] = 0xFFF72031; // 0xFfFf2138; // 0x100c AGC_Ctrl4
			//pWb35Reg->BB0C = 0xFFF72031;
			pltmp[4] = 0x0FacDCC5; // 0x1010 AGC_Ctrl5 // 20050927 0x0FacDCB7
			pltmp[5] = 0x00CAA333; // 0x00eaa333; // 0x1014 AGC_Ctrl6
			pltmp[6] = 0xF2211111; // 0x11111111; // 0x1018 AGC_Ctrl7
			pltmp[7] = 0x0FA3F0ED; // 0x101c AGC_Ctrl8
			pltmp[8] = 0x06443440; // 0x1020 AGC_Ctrl9
			pltmp[9] = 0xA8002A79; // 0xa9002A79; // 0x1024 AGC_Ctrl10
			pltmp[10] = 0x40000528; // 20050927 0x40000228
			pltmp[11] = 0x232D7F30; // 0x23457f30;// 0x102c A_ACQ_Ctrl
			//pWb35Reg->BB2C = 0x232D7F30;
			wbusb_reg_write_burst(priv,REG_BB+0x00, pltmp, 12, AUTO_INC);
	
			pltmp[0] = 0x00002c54; // 0x1030 B_ACQ_Ctrl
			//pWb35Reg->BB30 = 0x00002c54;
			pltmp[1] = 0x00C0D6C5; // 0x1034 A_TXRX_Ctrl
			pltmp[2] = 0x5B2C8769; // 0x1038 B_TXRX_Ctrl
			pltmp[3] = 0x00000000; // 0x103c 11a TX LS filter
			//pWb35Reg->BB3C = 0x00000000;
			pltmp[4] = 0x00003F29; // 0x1040 11a TX LS filter
			pltmp[5] = 0x0EFEFBFE; // 0x1044 11a TX LS filter
			pltmp[6] = 0x00332C1B; // 0x00453B24; // 0x1048 11b TX RC filter
			pltmp[7] = 0x0A00FEFF; // 0x0E00FEFF; // 0x104c 11b TX RC filter
			pltmp[8] = 0x2B106208; // 0x1050 MODE_Ctrl
			//pWb35Reg->BB50 = 0x2B106208;
			pltmp[9] = 0; // 0x1054
			//pWb35Reg->BB54 = 0x00000000;
			pltmp[10] = 0x52524242; // 0x64645252; // 0x1058 IQ_Alpha
			//pWb35Reg->BB58 = 0x52524242;
			pltmp[11] = 0xAA0AC000; // 0x105c DC_Cancel
			wbusb_reg_write_burst(priv,REG_BB+0x30, pltmp, 12, AUTO_INC);
			
			// 7230 - 5GHz : TODO
		break;
		case RF_AIROHA_2230: //We can test only this one yet
			pltmp[0] = 0X16764A77; // 0x1000 AGC_Ctrl1		//0x16765A77
			pltmp[1] = 0x9affafb2; // 0x1004 AGC_Ctrl2
			pltmp[2] = 0x55d00a04; // 0x1008 AGC_Ctrl3
			pltmp[3] = 0xFFFd203c; // 0xFFFb203a; // 0x100c AGC_Ctrl4 Modify for 33's 1.0.95.xxx version
			//pWb35Reg->BB0C = 0xFFFd203c;
			pltmp[4] = 0X0FBFDCc5; // 0X0FBFDCA0; // 0x1010 AGC_Ctrl5 //0x0FB2E0B7 Modify for 33's 1.0.95.xxx version
			pltmp[5] = 0x00caa332; // 0x00caa333; // 0x1014 AGC_Ctrl6 Modify for 33's 1.0.95.xxx version
			pltmp[6] = 0XF6632111; // 0XF1632112; // 0x1018 AGC_Ctrl7		//0xf6632112 Modify for 33's 1.0.95.xxx version
			pltmp[7] = 0x0FA3F0ED; // 0x101c AGC_Ctrl8
			pltmp[8] = 0x04C43640; // 0x1020 AGC_Ctrl9
			pltmp[9] = 0x00002A79; // 0x1024 AGC_Ctrl10
			pltmp[10] = 0X40000528;							//0x40000228
			pltmp[11] = 0x232dfF30; // 0x232A9F30; // 0x102c A_ACQ_Ctrl	//0x232a9730
			//pWb35Reg->BB2C = 0x232dfF30; //Modify for 33's 1.0.95.xxx version, antenna 1
			wbusb_reg_write_burst(priv,REG_BB+0, pltmp, 12, AUTO_INC);

			pltmp[0] = 0x00002C54; // 0x1030 B_ACQ_Ctrl
			//pWb35Reg->BB30 = 0x00002C54;
			pltmp[1] = 0x00C0D6C5; // 0x1034 A_TXRX_Ctrl
			pltmp[2] = 0x5B2C8769; // 0x1038 B_TXRX_Ctrl	//0x5B6C8769
			pltmp[3] = 0x00000000; // 0x103c 11a TX LS filter
			//pWb35Reg->BB3C = 0x00000000;
			pltmp[4] = 0x00003F29; // 0x1040 11a TX LS filter
			pltmp[5] = 0x0EFEFBFE; // 0x1044 11a TX LS filter
			pltmp[6] = 0x00332c1b; //BB48_DEFAULT_AL2230_11G; // 0x1048 11b TX RC filter 20060613.2
			//pWb35Reg->BB48 = BB48_DEFAULT_AL2230_11G; // 20051221 ch14 20060613.2
			pltmp[7] = 0x0a00feff; //BB4C_DEFAULT_AL2230_11G; // 0x104c 11b TX RC filter 20060613.2
			//pWb35Reg->BB4C = BB4C_DEFAULT_AL2230_11G; // 20060613.1 20060613.2
			pltmp[8] = 0x27106200; // 0x1050 MODE_Ctrl
			//pWb35Reg->BB50 = 0x27106200;
			pltmp[9] = 0; // 0x1054
			//pWb35Reg->BB54 = 0x00000000;
			pltmp[10] = 0x52524242; // 0x1058 IQ_Alpha
			//pWb35Reg->BB58 = 0x52524242;
			pltmp[11] = 0xAA0AC000; // 0x105c DC_Cancel
			wbusb_reg_write_burst(priv,REG_BB+0x30, pltmp, 12, AUTO_INC);

			wbusb_reg_write(priv,REG_BB+0x70,0x00000045);
			break;
	}
	DEBUG("[airoha] airoha_bb_proc_init end")
	return 0;
}
